# !/usr/bin/env python
# -*- coding: utf-8 -*-

# @File   : caserun.py
# @Time   : 2022/5/30 2:28 下午
# @Author : yujianhui
import json
from urllib.parse import parse_qsl,urlunparse
from frame.utils.xmlparse import xmlparse
from frame.utils.urlpathutil import get_path_from_dict, get_path_from_url, get_query_from_dict,parse
from frame.req import request
from frame.utils import pathutil
from frame.vo.casevo import CaseVo
from frame.utils import check
from frame.utils import regexutil

def get_case_vos(path_list:list[str])->list[CaseVo]:
    """
    获取路径下文件中所有的case
    :param path_list: 路径列表
    :return: list[CaseVo]
    """
    case_vos=[]
    for path in path_list:
        # 获取绝对路径
        abs_path=pathutil.path_join(pathutil.project_root_path(),path)
        # 获取绝对路径下的所有xml文件
        xml_abs_path=pathutil.all_file_path(abs_path,'.xml')
        # 获取文件中的case
        for xml in xml_abs_path:
            case_vo=xmlparse(xml)
            case_vos.extend(case_vo)
    return case_vos

class CasesRun:

    @staticmethod
    def case_run(case:CaseVo,params:dict,token):

        global res
        # 测试前，替换case中的参数, 替换请求中 url headers body 中的参数，url支持一个参数，headers的支持多个value，body支持多个
        CasesRun.__replace_params(case,params)

        # 执行测试
        res=CasesRun.__case_request(case,token)

        # 断言
        CasesRun.__result_check(case,res)

        # 测试后，保存需要的参数
        CasesRun.__save_params(res,params,case)

    @staticmethod
    def __case_request(case:CaseVo,token) ->json:
        req = case.request
        method = req.method
        if method == 'GET':
            return request.get(req,token)
        if method == 'POST':
            return request.post(req,token)

    @staticmethod
    def __result_check(case:CaseVo,res:dict):
        # 根据不同的断言类型进行断言
        for expect in case.expects:
            valid_type=expect.validType
            if valid_type == "equal":
                check.equal(expect.expected, res)

            elif valid_type == "contain":
                check.contain(expect.expected, res)

            elif valid_type == "regex":
                check.regex(expect.expected, res)

    @staticmethod
    def __replace_params(case:CaseVo,params:dict):
        """
        替换请求中 url headers body 中的参数
        :param case:
        :param params:
        :return:
        """
        if not params:
            return
        url=case.request.url
        headers=case.request.headers
        body=case.request.body
        pattern='\$\{.*\}'
        # 替换url中的参数
        if url:
            paser_result = parse(url)
            query = dict(parse_qsl(paser_result.query))
            for k,v in query.items():
                query[k]=CasesRun.__param_format(pattern,v,params)
            query = get_query_from_dict(query)
            case.request.url=urlunparse((paser_result.scheme,paser_result.netloc,paser_result.path,paser_result.params,query,paser_result.fragment))

        # 替换headers中的参数
        if headers:
            for k,v in headers.items():
                headers[k] = CasesRun.__param_format(pattern,v,params)
            case.request.headers=headers

        # 替换body中的参数
        if body:
            # json 格式
            if isinstance(body,dict):
                for k,v in body.items():
                    body[k] = CasesRun.__param_format(pattern, v, params)
            # 非json格式 ,k-v格式
            if isinstance(body,str) and '&' in body:
                dict_body=get_path_from_url(body)
                for k,v in dict_body.items():
                    dict_body[k]=CasesRun.__param_format(pattern, v, params)
                body=get_path_from_dict(dict_body)
            case.request.body=body

    @staticmethod
    def __param_format(pattern:str,string:str,params:dict)->str:
        # 替换字符串中的正则，__new_str 从params中获取
        regex_res = regexutil.search(pattern, string)
        if regex_res:
            param_key = regex_res.removeprefix('${').removesuffix('}')
            if param_key in params.keys():
                string = string.replace(regex_res, str(params[param_key]))
        return string

    @staticmethod
    def __save_params(res:dict,params:dict,case:CaseVo):
        variables=case.variables
        for var in variables:
            # key 和 value 校验
            if var.key in params.keys():
                raise RuntimeError("variable.key={} 已存在，请修改变量名".format(var.key))
            if not var.value.startswith('$result'):
                raise RuntimeError("variable.value格式错误，请以$result开头，并以jsonpath的格式设置value")
            values=var.value.split('.')
            temp_res=res
            # 保存变量
            for v in values[1:]:
                temp_res=CasesRun.__get_value_from_res(v,temp_res)
                if temp_res:
                    params[var.key]=temp_res
                else:
                    raise RuntimeError("从响应结果中未获取到变量；response={} ; variable.key={}".format(res,var.key))

    @staticmethod
    def __get_value_from_res(v:str,res):
        # 正则搜索是否是组数
        reg_res=regexutil.search('\\[[0-9]+\\]',v)
        if reg_res:
            # 获取数组索引和节点
            index=int(reg_res[1:-1])
            node=v[:v.index(reg_res)]
            return res[node][index]
        else:
            return res[v]

if __name__=='__main__':
    # value='$result.data.oa.ob[3].oc'
    # vs=value.split('.')
    # res={
    #     'd1':1,
    #     'data':{
    #         'oa1':1,
    #         'oa':{
    #             'ob':[
    #                 1,
    #                 2,
    #                 3,
    #                 {
    #                     'oc':'res_123'
    #                 }
    #             ]
    #         }
    #     }
    # }
    # for v in vs[1:]:
    #     res=save_param(v,res)
    # print(res)
    # print(res['data']['oa']['ob'][3]['oc'])
    pass



